import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {Observable, Subject} from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class CommonService {

  private authSubject = new Subject();
  private tokenSubject = new Subject();

  authObservable = this.authSubject.asObservable();
  tokenObservable = this.tokenSubject.asObservable();

  constructor(
    private http: HttpClient,
  ) {
  }

  privateMethod(token: string): Observable<any> {
    return this.http.get(`/user/private`, {
      headers: {
        Authorization: `bearer ${token}`
      },
      responseType: 'text'
    });
  }

  adminMethod(token: string): Observable<any> {
    return this.http.get(`/user/list`, {
      headers: {
        Authorization: `bearer ${token}`
      }
    });
  }

  publicMethod(): Observable<any> {
    return this.http.get(`/user/public`, {responseType: 'text'});
  }

  login(username, password): void {
    const params = new URLSearchParams();
    params.append('grant_type', 'password');
    params.append('username', username);
    params.append('password', password);
    this.tokenRequest(params);
  }

  refreshToken(): void {
    const tokenJson = localStorage.getItem('token');
    if (!tokenJson || !JSON.parse(tokenJson)[`refresh_token`]) {
      this.clear();
      return;
    }
    const params = new URLSearchParams();
    params.append('grant_type', 'refresh_token');
    params.append('refresh_token', JSON.parse(tokenJson)[`refresh_token`]);
    this.tokenRequest(params);
  }

  checkToken(token: string): void {
    this.http.get(`/oauth/check_token?token=${token}`, {
      headers: {
        Authorization: 'Basic ' + btoa('client:secret')
      }
    }).subscribe(res => {
      this.changeAuth(res);
    });
  }

  clear(): void {
    localStorage.removeItem('auth');
    this.changeAuth({});
    localStorage.removeItem('token');
    this.changeToken({});
  }

  tokenRequest(params: any): void {
    this.http.post(`/oauth/token`, params.toString(), {
      headers: {
        'Content-type': 'application/x-www-form-urlencoded; charset=utf-8',
        Authorization: 'Basic ' + btoa('client:secret')
      }
    }).subscribe(res => {
      this.changeToken(res);
    });
  }

  getAuth(): any {
    const authJson = localStorage.getItem('auth');
    if (!!authJson) {
      const auth = JSON.parse(authJson);
      if (auth.exp + 200000 < new Date().getTime()) {
        this.refreshToken();
      }
      return auth;
    }
    return {};
  }

  getToken(): any {
    const tokenJson = localStorage.getItem('token');
    if (!!tokenJson) {
      const token = JSON.parse(tokenJson);
      if (token.expires_in + 200000 < new Date().getTime()) {
        this.refreshToken();
      }
      return token;
    }
    return {};
  }

  changeAuth(res: any): void {
    if (!!res && res[`exp`]) {
      res[`exp`] = res[`exp`] * 1000;
    }
    localStorage.setItem('auth', JSON.stringify(res));
    this.authSubject.next(res);
  }

  changeToken(res: any): void {
    if (!!res && res[`expires_in`]) {
      res[`expires_in`] = new Date().getTime() + res[`expires_in`] * 1000;
      this.checkToken(res[`access_token`]);
    }
    localStorage.setItem('token', JSON.stringify(res));
    this.tokenSubject.next(res);
  }

  logout(): void {
    const token = this.getToken();
    const redirect = 'http://localhost:4200';
    this.http.get(`/oauth/logout?token=${token.access_token}&redirect=${redirect}`, {responseType: 'text'}).subscribe();
  }

  codeLogin(code: string): void {
    const params = new URLSearchParams();
    params.append('grant_type', 'authorization_code');
    params.append('redirect_uri', 'http://localhost:4200/login');
    params.append('code', code);
    this.http.post(`/oauth/token`, params.toString(), {
      headers: {
        'Content-type': 'application/x-www-form-urlencoded; charset=utf-8',
        Authorization: 'Basic ' + btoa('client:secret')
      }
    }).subscribe(res => {
      this.changeToken(res);
    });
  }
}
